home *** CD-ROM | disk | FTP | other *** search
Text File | 1989-04-27 | 10.6 KB | 301 lines | [TEXT/????] |
- ;;; $Header: polynm.scm,v 1.9 88/09/01 22:01:31 GMT cph Exp $
- ;;; last modified 8/20/88 (mh)
-
- ;;;; Dense Univariate Polynomial Arithmetic
- ;;; Procedures for manipulating polynomials. A polynomial is represented as
- ;;; a list of possibly-complex coefficients in ascending order, i.e.,
- ;;; a0 + a1s + a2s^2 + . . . + ans^n ==> (a0 a1 a2 . . . an)
-
- (if-mit
- (declare (usual-integrations = + - * /
- zero? 1+ -1+
- ;; truncate round floor ceiling
- sqrt exp log sin cos)))
-
- (define (add-2-polys p1 p2)
- (cond ((null? p1) p2)
- ((null? p2) p1)
- (else (cons (+ (car p1) (car p2))
- (add-2-polys (cdr p1) (cdr p2))))))
-
- (define add-polys (accumulation add-2-polys '()))
-
- (define (sub-2-polys p1 p2)
- (cond ((null? p1) (scale-poly -1 p2))
- ((null? p2) p1)
- (else (cons (- (car p1) (car p2))
- (sub-2-polys (cdr p1) (cdr p2))))))
-
- (define (sub-polys . polys)
- (cond ((null? polys) '())
- ((null? (cdr polys))
- (scale-poly -1 (car polys)))
- (else (sub-2-polys (car polys)
- (apply add-polys (cdr polys))))))
-
- (define (scale-poly factor poly)
- (map (lambda (x) (* x factor)) poly))
-
- (define (mul-2-polys p1 p2)
- (if (or (null? p1) (null? p2))
- '()
- (add-2-polys (scale-poly (car p1) p2)
- (cons 0 (mul-2-polys (cdr p1) p2)))))
-
- (define mul-polys (accumulation mul-2-polys '(1)))
-
- ;;; The following procedure returns a list of two lists, representing
- ;;; the quotient and the remainder polynomials. For example:
- ;;; (div-polys '(5 4 3 1) '(1 1)) ==> ((2 2 1) (3))
- ;;; corresponds to the division:
- ;;;
- ;;; s^3 + 3s^2 + 4s + 5 3
- ;;; --------------------- = s^2 + 2s + 2 + -------
- ;;; s + 1 s + 1
-
- (define (div-2-polys p1 p2)
- (if (< (length p1) (length p2))
- (list '() p1)
- (let ((nr (reverse p1)) (dr (reverse p2)))
- (let ((sf (/ (car nr) (car dr))))
- (let ((dp (div-2-polys (reverse
- (sub-2-polys (cdr nr)
- (scale-poly sf (cdr dr))))
- p2)))
- (cons (reverse (cons sf (reverse (car dp))))
- (cdr dp)))))))
-
- (define (div-polys . polys)
- (cond ((null? polys) '(() (0)))
- ((null? (cdr polys))
- (list '() '(1)))
- (else (div-2-polys (car polys)
- (apply mul-polys (cdr polys))))))
-
- (define (deriv-poly p) ; (a0 a1 ... aN) => (a1 2a2 ... NaN), (a0) => (0)
- (let ((n (length p)))
- (if (= n 1)
- (list 0)
- (generate-list (- n 1)
- (lambda (i)
- (let ((j (+ i 1)))
- (* j (list-ref p j))))))))
-
-
- (define (poly-value poly point)
- (if (null? poly) ;Horner's method
- 0
- (+ (car poly) (* point (poly-value (cdr poly) point)))))
-
- ;;; The following procedure returns a list of the complex roots of a
- ;;; polynomial described by a list of its coefficients in the syntax
- ;;; (b0 b1 b2 . . . bn) = b0 + b1*s + b2*s^2 + . . . + bn*s^n.
- ;;; Coefficients may be arbitrary complex or real numbers.
- ;;; Initial-guess is an optional complex number argument; if
- ;;; omitted, the default value is '(.1 . 1).
- ;;; Example:
- ;;; ==>(poly->roots '(.0626457 .407983 .548892 1.41505 .574432 1))
- ;;; ((-5.48521e-2 . .965945)(-.14361 . .596993)(-.177508 . 0)
- ;;; (-.14361 . -.596993)(-5.48521e-2 . -.965945)) in about 15 sec.
- ;;; The control structure of this algorithm is essentially the same
- ;;; as that described by Cuthbert, p40, which in turn derives from
- ;;; a calculator program developed by H-P.
-
- (define (poly->roots poly . initial-guess)
- (define (find-a-root poly)
- (define (poly-deriv poly point)
- (let iter ((lst (cdr poly)) (k 1))
- (if (null? lst)
- 0
- (+ (* k (car lst))
- (* point (iter (cdr lst) (1+ k)))))))
-
- (define (iter old-value-mag old-point new-point n m)
- (let ((new-value (poly-value poly new-point)))
- (let ((new-value-mag (magnitude new-value)))
- (cond ((>= new-value-mag old-value-mag)
- (cond ((< m 10)
- (iter old-value-mag
- old-point
- (+ old-point (/ (- new-point old-point) 4))
- n
- (1+ m)))
- ((< new-value-mag 1e-4) new-point)
- (else (list 'step-size-too-small poly))))
- ((< (magnitude (- new-point old-point)) 1e-5) new-point)
- ((< n 50)
- (iter new-value-mag
- new-point
- (- new-point
- (/ new-value (poly-deriv poly new-point)))
- (1+ n)
- 0))
- (else (list 'no-root-found poly))))))
- (let ((start-poly (poly-value poly initial-guess)))
- (iter (magnitude start-poly)
- initial-guess
- (- initial-guess
- (/ start-poly (poly-deriv poly initial-guess)))
- 0
- 0)))
-
- ;; continued on next page.
-
- (if (null? initial-guess)
- (set! initial-guess (make-rectangular .1 1))
- (set! initial-guess (car initial-guess)))
- (if (< (length poly) 2)
- '()
- (let ((root (find-a-root poly)))
- (if (number? root)
- (cons (heuristic-round-complex root)
- (poly->roots (car (div-polys poly (list (- root) 1)))
- initial-guess))
- root))))
-
- (define (roots->poly root-list)
- (if (null? root-list)
- (list 1)
- (let ((poly-of-rest (roots->poly (cdr root-list))))
- (sub-polys (cons 0 poly-of-rest)
- (scale-poly (car root-list) poly-of-rest)))))
-
- ;;; Create the zero-polynomial of unspecified degree
-
- (define (the-zero-polynomial)
- (list 0))
-
- ;;; Given a polynomial p, return the procedure that computes p(x)
-
- (define (poly->function p)
- (lambda (x) (poly-value p x)))
-
- ;;; Given a list of distinct abscissas xs = (x1 x2 ... xn) and a list
- ;;; of ordinates ys = (y1 y2 ... yn), return the Lagrange interpolation
- ;;; polynomial through the points (x1, y1), (x2, y2), ... (xn, yn).
-
- (define (make-interp-poly ys xs)
- ;; given a point list, return a poly that evalutates to 1 at the
- ;; first point and 0 at the others.
- (define (unity-at-first-point point-list)
- (let* ((x (car point-list))
- (px (reduce * (map (lambda (u) (- x u))
- (cdr point-list)))))
- (if (zero? px)
- (error "MAKE-INTERP-POLY: abscissas not distinct"))
- (scale-poly (/ 1 px) (roots->poly (cdr point-list)))))
- (let loop ((p (the-zero-polynomial))
- (points xs)
- (values ys))
- (if (null? values)
- p
- (let ((q (unity-at-first-point points)))
- (loop (add-polys p (scale-poly (car values) q))
- (left-circular-shift points)
- (cdr values))))))
-
- ;;; Given a function F, an interval [a, b], and a specified N > 0: we
- ;;; generate a polynomial P of order N (and degree N-1) that interpolates
- ;;; F at the "Chebyshev" points mapped onto [a, b]. We assume that the
- ;;; absolute error function E(x) = |F(x) - P(x)| is unimodal between
- ;;; adjacent interpolation points. Thus E(x) has altogether N+1 "bumps";
- ;;; the largest of these has height Bmax and the smallest has height Bmin.
- ;;; Of course Bmax is an error bound for the approximation of F by P on
- ;;; [a, b]; but Bmin has heuristic significance as a lower-bound for the
- ;;; reduced error that might be obtained by tuning the interpolation points
- ;;; to meet the equiripple criterion. We return the list (P Bmax Bmin).
-
- (define (get-poly-and-errors f a b n)
- (let* ((c (/ (+ a b) 2))
- (d (/ (- b a) 2))
- (imap (lambda (x) (+ c (* d x)))) ;map [-1, 1] -> [a, b]
- (points (map imap (cheb-root-list n)))
- (p (make-interp-poly (map f points) points))
- (abserr (lambda (x) (abs (- (f x) (poly-value p x)))))
- (abserr-a (abserr a))
- (abserr-b (abserr b))
- (max-and-min-bumps
- (let loop ((pts points)
- (bmax (max abserr-a abserr-b))
- (bmin (min abserr-a abserr-b)))
- (if (< (length pts) 2)
- (list bmax bmin)
- (let ((x0 (car pts)) (x1 (cadr pts)))
- (let ((bump (cadr (brent-max abserr x0 x1 1e-6))))
- (cond ((> bump bmax) (loop (cdr pts) bump bmin))
- ((< bump bmin) (loop (cdr pts) bmax bump))
- (else (loop (cdr pts) bmax bmin)))))))))
- (list p (car max-and-min-bumps) (cadr max-and-min-bumps))))
-
- (define (get-poly-function-and-errors f a b n)
- (let* ((c (/ (+ a b) 2))
- (d (/ (- b a) 2))
- (imap (lambda (x) (+ c (* d x)))) ;map [-1, 1] -> [a, b]
- (points (map imap (cheb-root-list n)))
- (p (lambda->procedure (lagrange (map f points) points)))
- (abserr (lambda (x) (abs (- (f x) (p x)))))
- (abserr-a (abserr a))
- (abserr-b (abserr b))
- (max-and-min-bumps
- (let loop ((pts points)
- (bmax (max abserr-a abserr-b))
- (bmin (min abserr-a abserr-b)))
- (if (< (length pts) 2)
- (list bmax bmin)
- (let ((x0 (car pts)) (x1 (cadr pts)))
- (let ((bump (cadr (brent-max abserr x0 x1 1e-6))))
- (cond ((> bump bmax) (loop (cdr pts) bump bmin))
- ((< bump bmin) (loop (cdr pts) bmax bump))
- (else (loop (cdr pts) bmax bmin)))))))))
- (list p (car max-and-min-bumps) (cadr max-and-min-bumps))))
-
- ;;; Given polynomial P(x), substitute x = r*y and compute the resulting
- ;;; polynomial Q(y) = P(y*r).
-
- (define (poly-arg-scale p r)
- (if (zero? r)
- (error "zero scale factor in POLY-ARG-SCALE"))
- (let loop ((result '()) (factor 1) (poly p))
- (if (null? poly)
- (reverse result)
- (loop (cons (* factor (car poly)) result)
- (* factor r)
- (cdr poly)))))
-
-
- ;;; Given polynomial P(x), substitute x = y+h and compute the resulting
- ;;; polynomial Q(y) = P(y+h)
- ;;; Note: We use here the fact that DIV-polys returns '() for the quotient
- ;;; if the degree of the denominator exceeds that of the numerator
-
- (define (poly-arg-shift p h)
- (let loop ((result '()) (poly p))
- (if (null? poly)
- (reverse result)
- (let ((q (div-polys poly (list (- h) 1))))
- (let ((quot (car q)) (rem (cadr q)))
- (loop (cons (poly-value rem h) result)
- quot))))))
-
-
- ;;; Alter the coefficients of polynomial P so that its domain [a,b] is
- ;;; mapped onto the canonical domain [-1,1].
-
- (define (poly-domain->canonical p a b)
- (if (<= b a)
- (error "bad interval: must have a < b in POLY-DOMAIN->CANONICAL"))
- (let ((c (/ (+ a b) 2)) (d (/ (- b a) 2)))
- ;; p(x) [a,b] --> p(y+c) = q(y) [-d,d] --> q(d*z) = r(z) [-1,1]
- (poly-arg-scale (poly-arg-shift p c) d)))
-
-
- ;;; Alter the coefficients of polynomial P so that its domain [-1,1]
- ;;; is mapped onto [a,b]. This is the inverse operation to
- ;;; POLY-DOMAIN->CANONICAL.
-
- (define (poly-domain->general p a b)
- (if (<= b a)
- (error "bad interval: must have a < b in POLY-DOMAIN->GENERAL"))
- (let ((c (/ (+ a b) 2)) (d (/ (- b a) 2)))
- (poly-arg-shift (poly-arg-scale p (/ 1 d)) (- c))))
-